/*** In The Name of Allah ***/
package com.codeanalysissys.backend.python;
import com.codeanalysissys.backend.cpp.CppCFGBuilder;
import com.codeanalysissys.backend.cpp.parser.CppLexer;
import com.codeanalysissys.backend.cpp.parser.CppParser;
import com.codeanalysissys.backend.graphs.cfg.CFNode;
import com.codeanalysissys.backend.graphs.cfg.CFPathTraversal;
import com.codeanalysissys.backend.graphs.cfg.ControlFlowGraph;
import com.codeanalysissys.backend.graphs.pdg.DDEdge;
import com.codeanalysissys.backend.graphs.pdg.DataDependenceGraph;
import com.codeanalysissys.backend.graphs.pdg.PDNode;
import com.codeanalysissys.backend.java.parser.JavaParser;
import com.codeanalysissys.backend.python.parser.PythonLexer;
import com.codeanalysissys.backend.python.parser.PythonParser;
import com.codeanalysissys.backend.python.parser.PythonParserBaseVisitor;
import ghaffarian.graphs.Edge;
import ghaffarian.nanologger.Logger;

import org.antlr.v4.runtime.ANTLRInputStream;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.misc.Interval;
import org.antlr.v4.runtime.tree.ParseTree;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;

/**
 * Data Dependence Graph (DDG) builder for Python programs.
 * The DDG is actually a subgraph of the Program Dependence Graph (PDG).
 * This implementation is based on ANTLRv4's Visitor pattern.
 *
 * @author WenLiang Peng
 */
public class PythonDDGBuilder {

	// Just for debugging
	private static String currentFile;

	// NOTE: This doesn't handle duplicate class names;
	//       yet assuming no duplicate class names is fair enough.
	//       To handle that, we should use 'Map<String, List<PythonClass>>'
	private static Map<String, PythonClass> allClassInfos;

	private static Map<String, List<MethodDefInfo>> methodDEFs;

	public static DataDependenceGraph build(InputStream inFile) throws IOException {
		ANTLRInputStream input = new ANTLRInputStream(inFile);
		PythonLexer lexer = new PythonLexer(input);
		CommonTokenStream tokens = new CommonTokenStream(lexer);
		PythonParser parser = new PythonParser(tokens);
		ParseTree parseTree = parser.root();

		// Extract the information of all given Python classes
//		Logger.info("\nExtracting class-infos ... ");
		allClassInfos = new HashMap<>();
		List<PythonClass[]> filesClasses = new ArrayList<>();
		// extract all the information from PythonMethod and PythonClass
		List<PythonClass> classesList = PythonClassExtractor.extractInfo(parseTree);
		filesClasses.add(classesList.toArray(new PythonClass[classesList.size()]));
		for (PythonClass cls : classesList) {
			if (cls != null)
				allClassInfos.put(cls.name, cls);
		}

//		Logger.info("Done.");

		// Initialize method DEF information
//		Logger.info("\nInitializing method-DEF infos ... ");
		methodDEFs = new HashMap<>();
		for (PythonClass[] classArray : filesClasses) {
			for (PythonClass cls : classArray) {
                if (cls != null) {
                    for (PythonMethod mtd : cls.getAllMethods()) {
                        List<MethodDefInfo> list = methodDEFs.get(mtd.NAME);
                        if (list == null) {
                            list = new ArrayList<>();
                            list.add(new MethodDefInfo(mtd.RETURN_TYPE, mtd.NAME, cls.name));
                            methodDEFs.put(mtd.NAME, list);
                        } else {
                            list.add(new MethodDefInfo(mtd.RETURN_TYPE, mtd.NAME, cls.name));
                            // no need to do 'methodDEFs.put(...)' again
                        }
					}
				}
			}
		}
//		Logger.info("Done.");
		// DDG initialization……
		DataDependenceGraph ddg = new DataDependenceGraph();
		//Iteratively, extract USE-DEF info for all program statements ...
		Map<ParserRuleContext, Object> pdNode = new IdentityHashMap<>();
		// 相对于hashmap标识性更高，不依赖于hascode和equal来进行对应的识别
		//
//		Logger.info("\nIterative DEF-USE analysis ... ");
		boolean changed;
		int iteration = 0;
		// walk the parseTree to analyse the def-use information(core)
		do {
			++iteration;
			changed = false;

			DefUseVisitor defUse = new DefUseVisitor(iteration, filesClasses.get(0), ddg, pdNode);
			defUse.visit(parseTree);
			changed |= defUse.changed;
//			Logger.debug("Iteration #" + iteration + ": " + (changed ? "CHANGED" : "NO-CHANGE"));
//			Logger.debug("\n========================================\n");
		} while (changed);
//		Logger.info("Done.");

		// Build control-flow graphs for all Python files including the extracted DEF-USE info ...
//		Logger.info("\nExtracting CFGs ... ");
		ControlFlowGraph cfg = PythonCFGBuilder.build(parseTree, "pdnode", pdNode);
//		Logger.info("Done.");
//		// Finally, traverse all control-flow paths and draw data-flow dependency edges ...
//		Logger.info("\nAdding data-flow edges ... ");
		addDataFlowEdges(cfg, ddg, ddg.getGlobalList());
		ddg.attachCFG(cfg);
		Logger.info("Done.\n");
		return ddg;
	}

	private enum analyseType {
		DEF,
		USE
	}

	public static DataDependenceGraph[] buildForAll(File[] files) throws IOException {
		// Parse all Python source files
		Logger.info("Parsing all source files ... ");
		ParseTree[] parseTrees = new ParseTree[files.length];
		for (int i = 0; i < files.length; ++i) {
			InputStream inFile = new FileInputStream(files[i]);
			ANTLRInputStream input = new ANTLRInputStream(inFile);
			PythonLexer lexer = new PythonLexer(input);
			CommonTokenStream tokens = new CommonTokenStream(lexer);
			PythonParser parser = new PythonParser(tokens);
			parseTrees[i] = parser.root();
		}
//		Logger.info("Done.");

		// Extract the information of all given Python classes
//		Logger.info("\nExtracting class-infos ... ");
		allClassInfos = new HashMap<>();
		List<PythonClass[]> filesClasses = new ArrayList<>();
		for (int i = 0; i < files.length; ++i) {
			// extract all the information from PythonMethod and PythonClass
			List<PythonClass> classesList = PythonClassExtractor.extractInfo(files[i].getPath(), parseTrees[i]);
			filesClasses.add(classesList.toArray(new PythonClass[classesList.size()]));
			for (PythonClass cls: classesList)
				allClassInfos.put(cls.name, cls);
		}
//		Logger.info("Done.");

		// Initialize method DEF information
//		Logger.info("\nInitializing method-DEF infos ... ");
		methodDEFs = new HashMap<>();
		for (PythonClass[] classArray: filesClasses) {
			for (PythonClass cls : classArray) {
				for (PythonMethod mtd : cls.getAllMethods()) {
					List<MethodDefInfo> list = methodDEFs.get(mtd.NAME);
					if (list == null) {
						list = new ArrayList<>();
						list.add(new MethodDefInfo(mtd.RETURN_TYPE, mtd.NAME, cls.name));
						methodDEFs.put(mtd.NAME, list);
					} else {
						list.add(new MethodDefInfo(mtd.RETURN_TYPE, mtd.NAME, cls.name));
						// no need to do 'methodDEFs.put(...)' again
					}
				}
			}
		}
//		Logger.info("Done.");
		// DDG initialization……
		DataDependenceGraph[] ddgs = new DataDependenceGraph[files.length];
		for (int i = 0; i < ddgs.length; ++i)
			ddgs[i] = new DataDependenceGraph(files[i].getName());
		//Iteratively, extract USE-DEF info for all program statements ...
		Map<ParserRuleContext, Object>[] pdNodes = new Map[parseTrees.length];
		for (int i = 0; i < parseTrees.length; ++i)
			pdNodes[i] = new IdentityHashMap<>();               // 相对于hashmap标识性更高，不依赖于hascode和equal来进行对应的识别
		//
//		Logger.info("\nIterative DEF-USE analysis ... ");
		boolean changed;
		int iteration = 0;
        // walk the parseTree to analyse the def-use information(core)
		do {
			++iteration;
			changed = false;
			for (int i = 0; i < files.length; ++i) {
				currentFile = files[i].getName();
				DefUseVisitor defUse = new DefUseVisitor(iteration, filesClasses.get(i), ddgs[i], pdNodes[i]);
				defUse.visit(parseTrees[i]);
				changed |= defUse.changed;
			}
//			Logger.debug("Iteration #" + iteration + ": " + (changed ? "CHANGED" : "NO-CHANGE"));
//			Logger.debug("\n========================================\n");
		} while (changed);
//		Logger.info("Done.");

		// Build control-flow graphs for all Python files including the extracted DEF-USE info ...
//		Logger.info("\nExtracting CFGs ... ");
		ControlFlowGraph[] cfgs = new ControlFlowGraph[files.length];
		for (int i = 0; i < files.length; ++i)
			cfgs[i] = PythonCFGBuilder.build(files[i].getName(), parseTrees[i], "pdnode", pdNodes[i]);
//		Logger.info("Done.");
//		// Finally, traverse all control-flow paths and draw data-flow dependency edges ...
//		Logger.info("\nAdding data-flow edges ... ");
		for (int i = 0; i < files.length; ++i) {
			addDataFlowEdges(cfgs[i], ddgs[i], ddgs[i].getGlobalList());
			ddgs[i].attachCFG(cfgs[i]);
		}
		Logger.info("Done.\n");
		return ddgs;
	}
	/**
	 * Returns the number of forward-slash ('/') characters in a given string.
	 */
	private static int countSlashes(String str) {
		int slashCount = 0;
		for (char chr: str.toCharArray())
			if (chr == '/')
				++slashCount;
		return slashCount;
	}
	/**
	 * Traverses each CFG and uses the extracted DEF-USE info
	 * to add Flow-dependence edges to the corresponding DDG.
	 */
	private static void addDataFlowEdges(ControlFlowGraph cfg, DataDependenceGraph ddg, Map<String, PDNode> globaList) {
		Set<CFNode> visitedDefs = new LinkedHashSet<>();
		for (CFNode entry: cfg.getAllMethodEntries()) {
			visitedDefs.clear();
			CFPathTraversal defTraversal = new CFPathTraversal(cfg, entry);
			while (defTraversal.hasNext()) {
				CFNode defCFNode = defTraversal.next();
				if (!visitedDefs.add(defCFNode)) {
					defTraversal.continueNextPath();
					continue;
				}
				PDNode defNode = (PDNode) defCFNode.getProperty("pdnode");
				if (defNode == null) {
					//Logger.debug("No PDNode: " + defCFNode);
					continue;
				}
				if (defNode.getAllDEFs().length == 0)
					continue;
				// first add any self-flows of this node
				for (String flow: defNode.getAllSelfFlows()) {
					ddg.addEdge(new Edge<>(defNode, new DDEdge(DDEdge.Type.FLOW, flow), defNode));
				}
				// now traverse the CFG for any USEs till a DEF
				Set<CFNode> visitedUses = new LinkedHashSet<>();
				for (String def: defNode.getAllDEFs()) {
					CFPathTraversal useTraversal = new CFPathTraversal(cfg, defCFNode);
					visitedUses.clear();
					CFNode useCFNode = useTraversal.next(); // skip start node
					visitedUses.add(useCFNode);
					while (useTraversal.hasNext()) {
						useCFNode = useTraversal.next();
						PDNode useNode = (PDNode) useCFNode.getProperty("pdnode");
						if (useNode == null) {
							//Logger.debug("No PDNode: " + useCFNode);
							continue;
						}
						if (useNode.hasDEF(def))
							useTraversal.continueNextPath(); // no need to continue this path
						if (!visitedUses.add(useCFNode))
							useTraversal.continueNextPath(); // no need to continue this path
						else {
							if (useNode.hasUSE(def)) {
								// judge the global vars
								if (globaList.containsKey(def)) {
									ddg.addEdge(new Edge<>(globaList.get(def), new DDEdge(DDEdge.Type.FLOW, def), useNode));
								} else {
									ddg.addEdge(new Edge<>(defNode, new DDEdge(DDEdge.Type.FLOW, def), useNode));
								}
							}
						}
					}
				}
			}
		}
	}

	/**
	 * Visitor class which performs iterative DEF-USE analysis for all program statements.
	 */
	private static class DefUseVisitor extends PythonParserBaseVisitor<String> {
		private static final int PARAM = 1;
		private static final int FIELD = 101;
		private static final int LOCAL = 202;
		private static final int OUTER = 303;
		private int iteration;
		private boolean changed;
		private boolean analysisVisit;
		private PythonClass[] classInfos;
		private DataDependenceGraph ddg;
		private Set<String> defList, useList, selfFlowList;
		private Map<ParserRuleContext, Object> pdNodes;
		private Deque<PythonClass> activeClasses;
		private MethodDefInfo methodDefInfo;
		private PythonField[] methodParams;
		private List<PythonField> localVars;
		// record the local vars
		private List<String> stmtLocalVars = new ArrayList<>();
		private List<String> stmtUses = new ArrayList<>();
		// record the self vars
		private boolean isSelf = false;

		public DefUseVisitor(int iter, PythonClass[] classInfos,
				DataDependenceGraph ddg, Map<ParserRuleContext, Object> pdNodes) {
			Logger.debug("FILE IS: " + currentFile);
			this.ddg = ddg;
			changed = false;
			iteration = iter;
			analysisVisit = false;
			this.pdNodes = pdNodes;
			this.classInfos = classInfos;
			defList = new LinkedHashSet<>();
			useList = new LinkedHashSet<>();
			selfFlowList = new LinkedHashSet<>();
			activeClasses = new ArrayDeque<>();
			methodDefInfo = null;
			methodParams = new PythonField[0];
			localVars = new ArrayList<>();           		 // localVars and for try and so on
		}
		private void analyseDefUse(PDNode node, ParseTree expression, analyseType type, int dataTypes) {
//			Logger.debug("--- ANALYSIS ---");
//			Logger.debug(node.toString());
			/* walk the experission to get the use and def list */
			analysisVisit = true;
			// analyse the def and use by walk the statements
			String expr = visit(expression);
//			Logger.debug(expr);
			// show the localvar analysed by the program
			if (dataTypes == LOCAL && type == analyseType.DEF) {
				if (defList != null && defList.size() != 0) {
					for (String temDef : defList) {
						stmtLocalVars.add(temDef);
					}
				}
			}
			if (dataTypes == LOCAL && type == analyseType.USE) {
				if (useList != null && useList.size() != 0) {
					for (String temUse : useList) {
						stmtUses.add(temUse);
					}
				}
			}
//			StringBuilder locVarsStr = new StringBuilder(256);
//			locVarsStr.append("LOCAL VARS = [");
//			for (PythonField lv: localVars)
//				locVarsStr.append(lv.NAME).append(", ");
//			locVarsStr.append("]");
//			Logger.debug(locVarsStr.toString());
			// judge whether the returned expr is the uses
			if (isUsableExpression(expr)) {
				useList.add(expr);
//				Logger.debug("USABLE");
			}
			analysisVisit = false;
//			Logger.debug("Changed = " + changed);
//			Logger.debug("DEFs = " + Arrays.toString(node.getAllDEFs()));
//			Logger.debug("USEs = " + Arrays.toString(node.getAllUSEs()));
			// add the def from the deflist to the node
			if (type.toString().equals("DEF")) {
				for (String def: defList) {
					int status = dataTypes;
					if (status > -1) {      // alreadly be defined
						if (status < 100) {
							methodDefInfo.setArgDEF(status, true);
	//						Logger.debug("Method defines argument #" + status);
						} else if (status == FIELD) {
							methodDefInfo.setStateDEF(true);
							if (def.startsWith("self."))
								def = def.substring(5);
							def = "$SELF." + def;
	//						Logger.debug("Method defines object state.");
						}
						changed |= node.addDEF(def);
					}
					else
						Logger.debug(def + " is not defined!");
				}
			}
//			Logger.debug("Changed = " + changed);
//			Logger.debug("DEFs = " + Arrays.toString(node.getAllDEFs()));
			// add the use from the uselist to the node
			if (type.toString().equals("USE")) {
				for (String use: useList) {
					int status = dataTypes;
					if (status > -1) {
						if (status == FIELD) {
							if (use.startsWith("self."))
								use = use.substring(5);
							use = "$SELF." + use;
						}
						changed |= node.addUSE(use);
					} else
						Logger.debug(use + " is not defined!");
				}
			}
//			Logger.debug("Changed = " + changed);
//			Logger.debug("USEs = " + Arrays.toString(node.getAllUSEs()));
			//
			for (String flow: selfFlowList) {
				int status = isDefined(flow);
				if (status > -1) {
					if (status == FIELD) {
						if (flow.startsWith("self."))
							flow = flow.substring(5);
						flow = "$SELF." + flow;
					}
					changed |= node.addSelfFlow(flow);
				} else
					Logger.debug(flow + " is not defined!");
			}
//			Logger.debug("Changed = " + changed);
//			Logger.debug("SELF_FLOWS = " + Arrays.toString(node.getAllSelfFlows()));
			defList.clear();
			useList.clear();
			selfFlowList.clear();
//			Logger.debug("----------------");
		}

		/**
		 * Check if a given symbol is a defined variable.
		 * This returns -1 if the symbol is not defined; otherwise,
		 * it returns 101 if the symbol is a class field,
		 * or returns 202 if the symbol is a local variable,
		 * or returns 303 if the symbol is an outer class field,
		 * or if the symbol is a method parameter, returns the index of the parameter.
		 */
		private int isDefined(String id) {
			// return the defined method params
			for (int i = 0; i < methodParams.length; ++i)
				if (methodParams[i].NAME.equals(id))
					return i;
			// return the defined local var
			for (PythonField local: localVars)
				if (local.NAME.equals(id))
					return LOCAL;
			if (id.startsWith("self."))
				id = id.substring(5);
			if (activeClasses != null && activeClasses.size() != 0) {
				for (PythonField field: activeClasses.peek().getAllFields())
					if (field.NAME.equals(id))
						return FIELD;
				for (PythonClass cls: activeClasses)
					for (PythonField field: cls.getAllFields())
						if (field.NAME.equals(id))
							return OUTER;
			}
			return -1;
		}
		private PythonClass findClass(String type) {
			return null;
		}
		/**
		 * Find and return matching method-definition-info.
		 * Returns null if not found.
		 */
		private MethodDefInfo findDefInfo(String callee, String name, JavaParser.ExpressionListContext ctx) {
			List<MethodDefInfo> list = methodDEFs.get(name);
			Logger.debug("METHOD NAME: " + name);
			Logger.debug("# found = " + (list == null ? 0 : list.size()));
			//
			if (list == null)
				return null;
			//
			if (list.size() == 1) { // only one candidate
				Logger.debug("SINGLE CANDIDATE");
				MethodDefInfo mtd = list.get(0);
				Logger.debug("WITH MATCHING PARAMS COUNT");
				return mtd;
			}
			//
			if (callee == null) { // no callee; so search for self methods
				Logger.debug("NO CALLEE");
				forEachDefInfo:
				for (MethodDefInfo mtd : list) {
					// check class-name
					boolean classNameMatch = false;
					for (PythonClass cls: activeClasses) {
						if (mtd.CLASS_NAME.equals(cls.name)) {
							classNameMatch = true;
							break;
						}
					}
					if (!classNameMatch)
						continue;
					// no param count to check
					// no param type to check
					return mtd;
				}
			} else if (isDefined(callee) > -1) { // has a defined callee
				Logger.debug("DEFINED CALLEE");

				PythonClass cls = allClassInfos.get(name);
				if (cls != null && cls.hasMethod(name)) {
					forEachDefInfo:
					for (MethodDefInfo mtd : list) {
						// check class-name
						if (!mtd.CLASS_NAME.equals(cls.name))
							continue;
						return mtd;
					}
					Logger.debug("METHOD DEF INFO NOT FOUND!");
				} else {
					Logger.debug((cls == null ?
							" NOT FOUND!" :
							"CLASS HAS NO SUCH METHOD!"));
				}
			} else { // has an undefined callee
//				Logger.debug("UNDEFINED CALLEE.");
				//
				// TODO: use a global retType for visiting expressions
				//
			}
			return null;
		}

		/**
		 * Find and return matching method-definition-info.
		 * Returns null if not found.
		 */
		private MethodDefInfo findDefInfo(String name, String type, PythonField[] params) {
			List<MethodDefInfo> infoList = methodDEFs.get(name);
			// if exist the same name then distinguish by the other elements
            if (infoList != null && infoList.size() > 1) {
				forEachInfo:
				for (MethodDefInfo info: infoList) {
					if (!info.CLASS_NAME.equals(activeClasses.peek().name))
						continue;
					if ((info.RET_TYPE == null && type != null) ||
							(info.RET_TYPE != null && type == null))
						continue;
					if (type != null && !type.startsWith(info.RET_TYPE))
						continue;
					if (params.length > 0)
						continue;
					return info;
				}
			} else if (infoList != null && infoList.size() == 1)
					return infoList.get(0);
			return null;
		}

		@Override
		public String visitClass_or_func_def_stmt(PythonParser.Class_or_func_def_stmtContext ctx) {
			//| decorator* (classdef | funcdef)
			if (ctx.classdef() != null) {
				for (PythonClass cls : classInfos){
					if (cls.name.equals(ctx.classdef().name().getText())){
						activeClasses.push(cls);
						visit(ctx.classdef().suite());
						activeClasses.pop();
						break;
					}
				}
			} else {
				visitChildren(ctx);
			}
			return null;
		}

		@Override
		public String visitClassdef(PythonParser.ClassdefContext ctx) {
			//   : CLASS name (OPEN_PAREN arglist? CLOSE_PAREN)? COLON suite
			//    ;
			if (ctx.suite() != null) {
				localVars.clear();
				methodParams = new PythonField[0];
				methodDefInfo = new MethodDefInfo(null,"",activeClasses.peek().name);
				return null;
			} else
				return visitChildren(ctx);
		}
		/*   DDG statement the same as the CFG statement     */
		@Override
		public String visitFuncdef(PythonParser.FuncdefContext ctx) {
			//  : ASYNC? DEF name OPEN_PAREN typedargslist? CLOSE_PAREN (ARROW test)? COLON suite
			//    ;
			PDNode entry;
			// if entry node is the first node(node class node)
			if (iteration == 1) {
				entry = new PDNode();
				entry.setLineOfCode(ctx.getStart().getLine());
				String retType = "void";
				if (ctx.test() != null)
					retType = ctx.test().getText();
                String args = "";
                if (ctx.typedargslist() != null) {
                    args = getOriginalCodeText(ctx.typedargslist());
                }
				String precode = (ctx.ASYNC() != null) ? ctx.ASYNC().getText() + ctx.DEF().getText() + ' ' + ctx.name().getText() : ctx.DEF().getText() + ' ' + ctx.name().getText();
				precode = (ctx.typedargslist() != null) ? precode + '(' + ctx.typedargslist().getText() + ')' : precode + "()";
				precode = (ctx.ARROW() != null) ? precode + ctx.ARROW().getText() : precode;
				precode = (ctx.test() != null) ? precode + ctx.test().getText() + ctx.COLON().getText() : precode + ctx.COLON().getText();
				entry.setCode(precode);
				entry.setProperty("name", ctx.name().getText());    // set the function name
				entry.setProperty("type", retType);                 // set the return type
				ddg.addVertex(entry);
				pdNodes.put(ctx, entry);
				// Extract all parameter IDs ,python has no type
				List<String> paramIDs = new ArrayList<>();
				if (ctx.typedargslist() != null) {
					for (PythonParser.Def_parametersContext temList :
							ctx.typedargslist().def_parameters()){
						for (PythonParser.Def_parameterContext temCtx :
							temList.def_parameter()){
							paramIDs.add(temCtx.named_parameter().name().getText());
						}
					}
				}
                if (ctx.typedargslist() != null && ctx.typedargslist().kwargs() != null) paramIDs.add("kwargs");
                if (ctx.typedargslist() != null && ctx.typedargslist().args() != null) paramIDs.add("args");
				methodParams = new PythonField[paramIDs.size()];
				for (int i = 0; i < methodParams.length; ++i)
					methodParams[i] = new PythonField(null, null, paramIDs.get(i));
				entry.setProperty("params", methodParams);
				//
				// Add initial DEF info: method entry nodes define the input-parameters
				for (String pid: paramIDs)
					changed |= entry.addDEF(pid);
			} else {
				// get the information stored before
				entry = (PDNode) pdNodes.get(ctx);
				methodParams = (PythonField[]) entry.getProperty("params");
			}
			methodDefInfo = findDefInfo((String) entry.getProperty("name"),
					(String) entry.getProperty("type"),	methodParams);
			if (methodDefInfo == null) {
				Logger.error("Method NOT FOUND!");
				Logger.error("NAME = " + (String) entry.getProperty("name"));
				Logger.error("TYPE = " + (String) entry.getProperty("type"));
				Logger.error("PARAMS = " + Arrays.toString(methodParams));
                if (activeClasses.peek() != null)
                    Logger.error("CLASS = " + activeClasses.peek().name);
				List list = methodDEFs.get((String) entry.getProperty("name"));
                if (list != null) {
                    for (int i = 0; i < list.size(); ++i)
                        Logger.error(list.get(i).toString());
                }
			}
			// Now visit method body ...
			localVars.clear();
			if (ctx.suite() != null) {
				visit(ctx.suite());
			}
			//
			localVars.clear();
			methodParams = new PythonField[0];
			return null;
		}
		@Override
		public String visitExpr_stmt(PythonParser.Expr_stmtContext ctx) {
            //: testlist_star_expr assign_part?
			// build the ddg node
			PDNode declr;
			if (iteration == 1) {
				declr = new PDNode();
				declr.setLineOfCode(ctx.getStart().getLine());
				declr.setCode(getOriginalCodeText(ctx));
				ddg.addVertex(declr);
				pdNodes.put(ctx, declr);
			} else
				declr = (PDNode) pdNodes.get(ctx);

			// now analysis the DEF-USE by visiting the expression
			if (ctx.assign_part() != null && ctx.assign_part().ASSIGN().size() == 1) {
				// exist the assign part then analyse the def-uses
				analyseDefUse(declr, ctx.assign_part(), analyseType.USE, LOCAL);
				analyseDefUse(declr, ctx.testlist_star_expr(), analyseType.DEF, LOCAL);
	 		} else if (ctx.assign_part() != null && ctx.assign_part().ASSIGN().size() > 1) {
				// exist many assign part
				for (int i = 0; i < ctx.assign_part().testlist_star_expr().size(); i++) {
					if (i == ctx.assign_part().testlist_star_expr().size() - 1) {
						analyseDefUse(declr, ctx.assign_part().testlist_star_expr(i), analyseType.USE, LOCAL);
					}
					analyseDefUse(declr, ctx.assign_part().testlist_star_expr(i), analyseType.DEF, LOCAL);
				}
				if (ctx.assign_part().yield_expr() != null) {
					analyseDefUse(declr, ctx.assign_part().yield_expr(), analyseType.USE, LOCAL);
				}
				analyseDefUse(declr, ctx.testlist_star_expr(), analyseType.DEF, LOCAL);
			}
			else {
				analyseDefUse(declr, ctx.testlist_star_expr(), analyseType.USE, LOCAL);
			}
			if (isSelf == true) {
				if (stmtLocalVars.size() != 0) {
					for (String temVar : stmtLocalVars) {
                        if (!temVar.equals("self") && methodDefInfo != null)
							methodDefInfo.addFieldDEF(temVar);
					}
				}
			}
			if (stmtUses.size() != 0 && stmtLocalVars.size() != 0 && isSelf == false) {
				// only one var
				if (stmtLocalVars.size() == 1) {
					for (String temUse : stmtUses) {
						PythonField temField = new PythonField(null,temUse,stmtLocalVars.get(0));
						localVars.add(temField);
					}
				} else {
					// there are many defs
					if (stmtUses.size() == stmtLocalVars.size()) {
						// python allow the definition sequencely
						for (String temUse : stmtUses) {
							for(String temDef : stmtLocalVars) {
								PythonField temField = new PythonField(null, temUse, temDef);
								localVars.add(temField);
							}
						}
					}
					for (String temDef : stmtLocalVars) {
						PythonField temField = new PythonField(null, stmtUses.get(0), temDef);
						localVars.add(temField);
					}
				}
			}
			isSelf = false;
			stmtUses.clear();
			stmtLocalVars.clear();
			return null;
		}

		@Override
		public String visitTry_stmt(PythonParser.Try_stmtContext ctx) {
			// TRY COLON suite (except_clause+ else_clause? finally_clause? | finally_clause)
			// create try Node first(although no use)
			PDNode tryNode = new PDNode();
			tryNode.setLineOfCode(ctx.getStart().getLine());
			tryNode.setCode("try:");
			ddg.addVertex(tryNode);
			pdNodes.put(ctx, tryNode);
			// now walk the body
			visit(ctx.suite());
			// analyse the elif node
			if (ctx.except_clause() != null) {
				//: EXCEPT (test ({this.CheckVersion(2)}? COMMA name {this.SetVersion(2);} | {this.CheckVersion(3)}? AS name {this.SetVersion(3);})?)? COLON suite
				PDNode[] exceptNodes = new PDNode[ctx.except_clause().size()];
				if (iteration == 1) {
					for (int i = 0; i < ctx.except_clause().size(); i++) {
                        PDNode pdNode = new PDNode(); // 初始化一个PDNode对象,这里的PDNode的数组都是空的,因此不初始化不行
						PythonParser.Except_clauseContext temCtx = ctx.except_clause(i);
                        if (temCtx.test() != null) {
                            String code = "except " + temCtx.test().getText();
                            code = (temCtx.AS() != null) ? code + " as " + temCtx.name().getText() + ":" : code + ":";
                            pdNode.setLineOfCode(ctx.except_clause(i).getStart().getLine());
                            pdNode.setCode(code);
                        } else {
                            String code = "except:";
                            pdNode.setLineOfCode(ctx.except_clause(i).getStart().getLine());
                            pdNode.setCode(code);
                        }
                        ddg.addVertex(pdNode);
                        pdNodes.put(ctx.except_clause(i), pdNode);
                        exceptNodes[i] = pdNode;
					}
				} else {
					for (int i = 0; i < ctx.except_clause().size(); i++) {
						exceptNodes[i] = (PDNode) pdNodes.get(ctx.except_clause(i));
					}
				}
				// analyse the elif use and def
				for (int i = 0; i < ctx.except_clause().size(); i++) {
                    if (ctx.except_clause(i).test() != null)
                        analyseDefUse(exceptNodes[i], ctx.except_clause(i).test(), analyseType.USE, LOCAL);
                    if (ctx.except_clause(i).name() != null && ctx.except_clause(i).test() != null) {
						analyseDefUse(exceptNodes[i], ctx.except_clause(i).test(), analyseType.DEF, LOCAL);
					}
				}
				// analyse the finally part
				if (ctx.finally_clause() != null) {
					visit(ctx.finally_clause().suite());
				}
				// analyse the else part
				if (ctx.else_clause() != null) {
					visit(ctx.else_clause().suite());
				}
			}
			return null;
		}
		@Override
		public String visitWith_stmt(PythonParser.With_stmtContext ctx) {
			// ASYNC? WITH with_item (COMMA with_item)* COLON suite
			// deal with the with node first
			PDNode withNode;
			if (iteration == 1) {
				withNode = new PDNode();
				withNode.setLineOfCode(ctx.getStart().getLine());
				String code = (ctx.ASYNC()!=null) ? "async with " : "with ";
				code = code + tranWithItem(ctx.with_item(0)) + " ";
				if (ctx.with_item().size() > 1){
					for (int i = 1; i < ctx.with_item().size(); i++){
						code = code + ", " + tranWithItem(ctx.with_item(i));
					}
					code = code + ":";
				}
				withNode.setCode(code);
				ddg.addVertex(withNode);
				pdNodes.put(ctx, withNode);
			} else {
				withNode = (PDNode) pdNodes.get(ctx);
			}
			// now analyse the def-use
			for (PythonParser.With_itemContext temCtx : ctx.with_item()) {
				if (temCtx.AS() != null) {
					analyseDefUse(withNode, temCtx.expr(), analyseType.DEF, LOCAL);
				}
				analyseDefUse(withNode, temCtx.test(), analyseType.USE, LOCAL);
			}
			// and then walk the body
			visit(ctx.suite());
			return null;
		}

		@Override
		public String visitPrint_stmt(PythonParser.Print_stmtContext ctx) {
			//  {this.CheckVersion(2)}? PRINT ((test (COMMA test)* COMMA?)
			//                       | RIGHT_SHIFT test ((COMMA test)+ COMMA?)) {this.SetVersion(2);}
			PDNode printNode;
			if (iteration == 1) {
				printNode = new PDNode();
				printNode.setLineOfCode(ctx.getStart().getLine());
				printNode.setCode(getOriginalCodeText(ctx));
				ddg.addVertex(printNode);
				pdNodes.put(ctx, printNode);
			} else
				printNode = (PDNode) pdNodes.get(ctx);
			// now analyse the def-use
			if (ctx.test() != null) {
				for (PythonParser.TestContext temCtx : ctx.test()) {
					analyseDefUse(printNode, temCtx, analyseType.USE, LOCAL);
				}
			}
			return null;
		}

		@Override
		public String visitDel_stmt(PythonParser.Del_stmtContext ctx) {
			// DEL exprlist
			PDNode delNode;
			if (iteration == 1) {
				delNode = new PDNode();
				delNode.setLineOfCode(ctx.getStart().getLine());
				delNode.setCode(getOriginalCodeText(ctx));
				ddg.addVertex(delNode);
				pdNodes.put(ctx, delNode);
			} else
				delNode = (PDNode) pdNodes.get(ctx);
            // analyse the use and def
			analyseDefUse(delNode, ctx.exprlist(), analyseType.USE, LOCAL);
			return null;
		}
		// there is no need to analyse the pass_stmt for it is impossible to generate edge
		// the same to break and continue statements
		@Override
		public String visitReturn_stmt(PythonParser.Return_stmtContext ctx) {
			// RETURN testlist?
			PDNode returnNode;
			if (iteration == 1) {
				returnNode = new PDNode();
				returnNode.setLineOfCode(ctx.getStart().getLine());
				returnNode.setCode(getOriginalCodeText(ctx));
				ddg.addVertex(returnNode);
				pdNodes.put(ctx, returnNode);
			} else
				returnNode = (PDNode) pdNodes.get(ctx);
			// analyse the use and def
			if (ctx.testlist() != null)
				analyseDefUse(returnNode, ctx.testlist(), analyseType.USE, LOCAL);
			return null;
		}
		@Override
		public String visitRaise_stmt(PythonParser.Raise_stmtContext ctx) {
			//  RAISE (test (COMMA test (COMMA test)?)?)? (FROM test)?
			PDNode RaiseNode;
			if (iteration == 1) {
				RaiseNode = new PDNode();
				RaiseNode.setLineOfCode(ctx.getStart().getLine());
                if (ctx.test(0) != null) {
                    String code = "raise " + ctx.test(0).getText() + " ";
                    code = (ctx.FROM() != null) ? code + "from " + ctx.test(1).getText() : code;
                    RaiseNode.setCode(code);
                } else {
                    String code = "raise";
                    RaiseNode.setCode(code);
                }
				ddg.addVertex(RaiseNode);
				pdNodes.put(ctx, RaiseNode);
			} else
				RaiseNode = (PDNode) pdNodes.get(ctx);
			// analyse the use and def
			if (ctx.test() != null) {
				for (PythonParser.TestContext temCtx : ctx.test()) {
					analyseDefUse(RaiseNode, temCtx, analyseType.USE, LOCAL);
				}
			}
			return null;
		}
		@Override
		public String visitYield_stmt(PythonParser.Yield_stmtContext ctx) {
			// yield_expr
			PDNode yieldNode;
			if (iteration == 1) {
				yieldNode = new PDNode();
				yieldNode.setLineOfCode(ctx.getStart().getLine());
				yieldNode.setCode(getOriginalCodeText(ctx));
				ddg.addVertex(yieldNode);
				pdNodes.put(ctx, yieldNode);
			} else
				yieldNode = (PDNode) pdNodes.get(ctx);
			// analyse the use and def
			if (ctx.yield_expr() != null) {
				analyseDefUse(yieldNode, ctx.yield_expr(), analyseType.USE, LOCAL);
			}
			return null;
		}
		@Override
		public String visitImport_stmt(PythonParser.Import_stmtContext ctx) {
			// IMPORT dotted_as_names
			PDNode importNode;
			if (iteration == 1) {
				importNode = new PDNode();
				importNode.setLineOfCode(ctx.getStart().getLine());
				String code = "import ";
				for (int i = 0; i < ctx.dotted_as_names().dotted_as_name().size(); i++){
					if (i == 0){
						code = code + ctx.dotted_as_names().dotted_as_name(i).getText();
					}else {
						code = code + ", " + ctx.dotted_as_names().dotted_as_name(i).getText();
					}
				}
				importNode.setCode(code);
				ddg.addVertex(importNode);
				pdNodes.put(ctx, importNode);
			} else
				importNode = (PDNode) pdNodes.get(ctx);
			// analyse the use and def
			analyseDefUse(importNode, ctx.dotted_as_names(), analyseType.DEF, OUTER);
			return "import " + ctx.dotted_as_names().getText();
		}
		@Override
		public String visitFrom_stmt(PythonParser.From_stmtContext ctx) {
			// FROM ((DOT | ELLIPSIS)* dotted_name | (DOT | ELLIPSIS)+)
			//      IMPORT (STAR | OPEN_PAREN import_as_names CLOSE_PAREN | import_as_names)
			PDNode fromNode;
			if (iteration == 1) {
				fromNode = new PDNode();
				fromNode.setLineOfCode(ctx.getStart().getLine());
				String code = "from ";
				for(int i = 0; i < ctx.DOT().size(); i++){
					code = code + ctx.DOT(i).getText();
				}
				code = (ctx.dotted_name() != null) ? code + ctx.dotted_name().getText() : code;
				code = code + " import ";
				if (ctx.STAR() != null){
					code = code + ctx.STAR().getText();
				}else{
					if (ctx.import_as_names() != null){
						assert ctx.import_as_names() instanceof PythonParser.Import_as_namesContext;
						code = (ctx.OPEN_PAREN() != null) ? code + '(' : code;
						int com_length = ctx.import_as_names().import_as_name().size();
						for (int i = 0; i < ctx.import_as_names().import_as_name().size(); i++){
							PythonParser.Import_as_nameContext temAsName = ctx.import_as_names().import_as_name(i);
							code = (temAsName.AS() != null) ? code + temAsName.name(0).getText() + " as " + temAsName.name(1).getText() : code + temAsName.name(0).getText();
							if (--com_length != 0) code = code + ',';
						}
					}else{
						//这种情况不存在
					}
				}
				fromNode.setCode(code);
				ddg.addVertex(fromNode);
				pdNodes.put(ctx, fromNode);
			} else
				fromNode = (PDNode) pdNodes.get(ctx);
			// now analyse the use and def
			if (ctx.dotted_name() != null) {
				analyseDefUse(fromNode, ctx.dotted_name(), analyseType.DEF, OUTER);
			}
			if (ctx.import_as_names() != null) {
				analyseDefUse(fromNode, ctx.import_as_names(), analyseType.DEF, OUTER);
			}
			return null;
		}
		@Override
		public String visitGlobal_stmt(PythonParser.Global_stmtContext ctx) {
			// GLOBAL name (COMMA name)*
			PDNode globalNode;
			if (iteration == 1) {
				globalNode = new PDNode();
				globalNode.setLineOfCode(ctx.getStart().getLine());
				globalNode.setCode(getOriginalCodeText(ctx));
				ddg.addVertex(globalNode);
				pdNodes.put(ctx, globalNode);
			} else
				globalNode = (PDNode) pdNodes.get(ctx);
			// analyse the use and def
			for (PythonParser.NameContext temCtx : ctx.name()) {
				analyseDefUse(globalNode, temCtx, analyseType.DEF, LOCAL);
			}
			// record the global vars
			for (PythonParser.NameContext temCtx : ctx.name()) {
				ddg.getGlobalList().put(temCtx.getText(),globalNode);
			}
			return ctx.getText();
		}

		@Override
		public String visitAssert_stmt(PythonParser.Assert_stmtContext ctx) {
			// ASSERT test (COMMA test)?
			PDNode assertNode;
			if (iteration == 1) {
				assertNode = new PDNode();
				assertNode.setLineOfCode(ctx.getStart().getLine());
				String code = "assert " + ctx.test(0).getText();
				for (int i = 1; i < ctx.test().size(); i++){
					code = code + ", " + ctx.test(i).getText();
				}
				assertNode.setCode(code);
				ddg.addVertex(assertNode);
				pdNodes.put(ctx, assertNode);
			} else
				assertNode = (PDNode) pdNodes.get(ctx);
			for (PythonParser.TestContext temTest : ctx.test()) {
				analyseDefUse(assertNode, temTest, analyseType.USE, LOCAL);
			}
			return null;
		}

		@Override
		public String visitNonlocal_stmt(PythonParser.Nonlocal_stmtContext ctx) {
			// {this.CheckVersion(3)}? NONLOCAL name (COMMA name)* {this.SetVersion(3);
			PDNode nonlocalNode;
			if (iteration == 1) {
				nonlocalNode = new PDNode();
				nonlocalNode.setLineOfCode(ctx.getStart().getLine());
				String code = "nonlocal " + ctx.name(0).getText();
				for (int i = 1; i < ctx.name().size(); i++){
					code = code + ", " + ctx.name(i).getText();
				}
				nonlocalNode.setCode(code);
				ddg.addVertex(nonlocalNode);
				pdNodes.put(ctx, nonlocalNode);
			} else {
				nonlocalNode = (PDNode) pdNodes.get(ctx);
			}
			// Now analyse the DEF-USE
			for (PythonParser.NameContext temName : ctx.name()) {
				analyseDefUse(nonlocalNode, temName, analyseType.USE, LOCAL);
			}
			return ctx.getText();
		}
		// exec node has no data dependency
		@Override
		public String visitIf_stmt(PythonParser.If_stmtContext ctx) {
			//IF cond=test COLON suite elif_clause* else_clause?
			PDNode ifNode;
			if (iteration == 1) {
				ifNode = new PDNode();
				ifNode.setLineOfCode(ctx.getStart().getLine());
				ifNode.setCode("if " + ctx.test().getText() + ctx.COLON().getText());
				ddg.addVertex(ifNode);
				pdNodes.put(ctx, ifNode);
			} else {
				ifNode = (PDNode) pdNodes.get(ctx);
			}
			// Now analyse the ifNode's def-uses namely the test model
			analyseDefUse(ifNode, ctx.test(), analyseType.USE, LOCAL);
			//then walk the body
			visit(ctx.suite());
			// analyse the elif node
			if (ctx.elif_clause() != null) {
				PDNode[] elifNodes = new PDNode[ctx.elif_clause().size()];
				if (iteration == 1) {
					for (int i = 0; i < ctx.elif_clause().size(); i++) {
                        PDNode pdNode = new PDNode(); // 初始化一个PDNode对象,这里的PDNode的数组都是空的,因此不初始化不行
						PythonParser.Elif_clauseContext temCtx = ctx.elif_clause(i);
                        pdNode.setCode("elif " + temCtx.test().getText() + temCtx.COLON().getText());
                        ddg.addVertex(pdNode);
                        pdNodes.put(ctx.elif_clause(i), pdNode);
                        elifNodes[i] = pdNode;
					}
				} else {
					for (int i = 0; i < ctx.elif_clause().size(); i++) {
						elifNodes[i] = (PDNode) pdNodes.get(ctx.elif_clause(i));
					}
				}
				// analyse the elif use and def of the elif node
				for (int i = 0; i < ctx.elif_clause().size(); i++) {
					analyseDefUse(elifNodes[i], ctx.elif_clause(i).test(), analyseType.USE, LOCAL);
				}
				// and then walk the body of the elif body
				for (int i = 0; i < ctx.elif_clause().size(); i++) {
					visit(ctx.elif_clause(i).suite());
				}
				// deal with the else
				if (ctx.else_clause() != null) {
					visit(ctx.else_clause());
				}
			}
			return null;
		}

		@Override
		public String visitWhile_stmt(PythonParser.While_stmtContext ctx) {
			// WHILE test COLON suite else_clause?
			PDNode whileNode;
			if (iteration == 1) {
				whileNode = new PDNode();
				whileNode.setLineOfCode(ctx.getStart().getLine());
				whileNode.setCode("while " + getOriginalCodeText(ctx.test()) + ctx.COLON().getText());
				ddg.addVertex(whileNode);
				pdNodes.put(ctx, whileNode);
			} else
				whileNode = (PDNode) pdNodes.get(ctx);
			// now analyse the DEF-USE by visiting the test condition
			analyseDefUse(whileNode, ctx.test(), analyseType.USE, LOCAL);
			// now walk the body
			visit(ctx.suite());
			// and then analyse the else situation
			if (ctx.else_clause() != null) {
				visit(ctx.else_clause().suite());
			}
			return "";                            // because the python has many bodies,so return null
		}

		@Override
		public String visitFor_stmt(PythonParser.For_stmtContext ctx) {
			// | ASYNC? FOR exprlist IN testlist COLON suite else_clause?
			PDNode forNode;
			if (iteration == 1) {
				forNode = new PDNode();
				forNode.setLineOfCode(ctx.getStart().getLine());
				String code = (ctx.ASYNC() != null) ? "async for "+ctx.exprlist().getText()+" in "+ctx.testlist().getText()+":" : "for "+ctx.exprlist().getText()+" in "+ctx.testlist().getText()+":";
				forNode.setCode(code);
				ddg.addVertex(forNode);
				pdNodes.put(ctx, forNode);
			} else
				forNode = (PDNode) pdNodes.get(ctx);
			// now analyse the DEF-USE by visiting the test condition
			analyseDefUse(forNode, ctx.testlist(), analyseType.USE, LOCAL);
			analyseDefUse(forNode, ctx.exprlist(), analyseType.DEF, LOCAL);
			visit(ctx.suite());
			// deal with the else_clause
			if (ctx.else_clause() != null) {
				visit(ctx.else_clause());
			}
			return "";
		}
		// finished now the last are all the statements that not of the cfg

		@Override
		public String visitLogical_test(PythonParser.Logical_testContext ctx) {
//			logical_test
//			       : comparison
//					| NOT logical_test
//					| logical_test op=AND logical_test
//					| logical_test op=OR logical_test
//			;
			if (ctx.comparison() != null) {
				visit(ctx.comparison());
			} else if (ctx.NOT() != null && ctx.logical_test() != null) {
				String temStr = visit(ctx.logical_test(0));
				return "not " + temStr;
			} else if (ctx.op != null && ctx.op.getText().equals("AND")){
				String temStr1 = visit(ctx.logical_test(0));
				String temStr2 = visit(ctx.logical_test(1));
				return temStr1 + " and " + temStr2;
			} else if (ctx.op != null && ctx.op.getText().equals("OR")){
				String temStr1 = visit(ctx.logical_test(0));
				String temStr2 = visit(ctx.logical_test(1));
				return temStr1 + " or " + temStr2;
			}
			return "";
		}

		@Override
		public String visitComparison(PythonParser.ComparisonContext ctx) {
			//comparison
			//    : comparison (LESS_THAN | GREATER_THAN | EQUALS | GT_EQ | LT_EQ | NOT_EQ_1 | NOT_EQ_2 | optional=NOT? IN | IS optional=NOT?) comparison
			//    | expr
			//    ;
			if (ctx.expr() != null) {
				String temExpr = visit(ctx.expr());
				return temExpr;
			} else if (ctx.comparison() != null) {
				// situation two
				// situation one
				String temStr1 = visit(ctx.comparison(0));
				String temStr2 = visit(ctx.comparison(1));
				if (ctx.LESS_THAN() != null) {
					return temStr1 + " < " + temStr2;
				} else if (ctx.GREATER_THAN() != null) {
					return temStr1 + " > " + temStr2;
				} else if (ctx.EQUALS() != null) {
					return temStr1 + " == " + temStr2;
				} else if (ctx.GT_EQ() != null) {
					return temStr1 + " >= " + temStr2;
				} else if (ctx.LT_EQ() != null) {
					return temStr1 + " <= " + temStr2;
				} else if (ctx.NOT_EQ_1() != null) {
					return temStr1 + " <> " + temStr2;
				} else if (ctx.NOT_EQ_2() != null) {
					return temStr1 + " != " + temStr2;
				} else if (ctx.IN() != null) {
					if (ctx.optional != null && ctx.optional.getText() == "not") {
						return temStr1 + " not in " + temStr2;
					} else {
						return temStr1 + " in " + temStr2;
					}
				} else if (ctx.IS() != null) {
					if (ctx.optional != null && ctx.optional.getText() == "not") {
						return temStr1 + " is in " + temStr2;
					} else {
						return temStr1 + " in " + temStr2;
					}
				}
			} else {
				// null
				return "";
			}
			return "";
		}
		@Override
		public String visitExpr(PythonParser.ExprContext ctx) {
			//    : AWAIT? atom trailer*
			//    | <assoc=right> expr op=POWER expr
			//    | op=(ADD | MINUS | NOT_OP) expr
			//    | expr op=(STAR | DIV | MOD | IDIV | AT) expr
			//    | expr op=(ADD | MINUS) expr
			//    | expr op=(LEFT_SHIFT | RIGHT_SHIFT) expr
			//    | expr op=AND_OP expr
			//    | expr op=XOR expr
			//    | expr op=OR_OP expr
			//    ;
			// judge the self var
            if (ctx.atom() != null) {
                if (ctx.atom().name() != null) {
                    if (ctx.atom() != null && ctx.atom().name() != null && ctx.atom().name().NAME() != null) {
                        String text = ctx.atom().name().NAME().getText();
                        if (text.equals("self")) {
                            // 执行相关操作
                            isSelf = true;
                        }
                    }
                }
            }

			if (ctx.atom() != null) {
				String temStr = visit(ctx.atom());
				if (ctx.trailer() != null) {
					for (PythonParser.TrailerContext temCtx : ctx.trailer()) {
						temStr = temStr + visit(temCtx);
					}
				}
				if (ctx.AWAIT() != null)
					return "await " + temStr;
				else
					return temStr;
			} else if (ctx.op != null) {
				if (ctx.expr().size() == 2){
					// two sizes of arg
					return visit(ctx.expr(0)) + ctx.op.getText() + visit(ctx.expr(1));
				} else {
					// just one arg
					return ctx.op.getText() + visit(ctx.expr(0));
				}
			} else {
				// no op
				return null;
			}
		}
		@Override
		public String visitAtom(PythonParser.AtomContext ctx) {
			//    : OPEN_PAREN (yield_expr | testlist_comp)? CLOSE_PAREN
			//    | OPEN_BRACKET testlist_comp? CLOSE_BRACKET
			//    | OPEN_BRACE dictorsetmaker? CLOSE_BRACE
			//    | REVERSE_QUOTE testlist COMMA? REVERSE_QUOTE
			//    | ELLIPSIS
			//    | name
			//    | PRINT
			//    | EXEC
			//    | MINUS? number
			//    | NONE
			//    | STRING+
			//    ;
			if (ctx.OPEN_PAREN() != null) {
				if (ctx.yield_expr() != null)
					return '('+ visit(ctx.yield_expr()) +')';
				else if (ctx.testlist_comp() != null) {
					return '('+ visit(ctx.testlist_comp()) +')';
				} else {
					return "()";
				}
			} else if (ctx.OPEN_BRACKET() != null) {
				if (ctx.testlist_comp() != null) {
					return '['+ visit(ctx.testlist_comp()) +']';
				}  else {
					return "[]";
				}
			} else if (ctx.OPEN_BRACE() != null) {
				if (ctx.dictorsetmaker() != null) {
					return '{' + visit(ctx.dictorsetmaker()) + '}';
				} else {
					return "{}";
				}
			} else if (ctx.REVERSE_QUOTE() != null && ctx.REVERSE_QUOTE().size() != 0) {
				if (ctx.COMMA() != null) {
					return "`" + visit(ctx.testlist()) + "," +"`";
				} else {
					return "`" + visit(ctx.testlist()) +"`";
				}
			} else if (ctx.ELLIPSIS() != null) {
				return "...";
			} else if (ctx.name() != null) {
				return visit(ctx.name());
			} else if (ctx.PRINT() != null) {
				return "print";
			}else if (ctx.EXEC() != null) {
				return "exec";
			}else if (ctx.number() != null) {
				if (ctx.MINUS() != null) {
					return "-" + visit(ctx.number());
				} else {
					return visit(ctx.number());
				}
			} else if (ctx.NONE() != null) {
				return "None";
			} else if (ctx.STRING() != null) {
				String tem = "";
				for (int i = 0; i < ctx.STRING().size(); i++) {
					tem = tem + ctx.STRING(i).getText();
				}
				return tem;
			}
			return "";
		}
		@Override
		public String visitDictorsetmaker(PythonParser.DictorsetmakerContext ctx) {
			//  : (test COLON test | POWER expr) (COMMA (test COLON test | POWER expr))* COMMA? // key_datum_list
			//    | test COLON test comp_for                                                      // dict_comprehension
			//    | testlist_comp
			//    ;
			if(ctx.testlist_comp() != null){
				return visit(ctx.testlist_comp());
			} else if (ctx.comp_for() != null) {
				return visit(ctx.test(0)) + "," + visit(ctx.test(1)) + visit(ctx.comp_for());
			} else {
				String temStr = "";
				for (PythonParser.TestContext temCtx : ctx.test()) {
					temStr = temStr + visit(temCtx);
				}
				for (PythonParser.ExprContext temExpr : ctx.expr()) {
					temStr = temStr + visit(temExpr);
				}
				return temStr;
			}
		}

		@Override
		public String visitTestlist(PythonParser.TestlistContext ctx) {
			//  : test (COMMA test)* COMMA?
			String temStr = "";
			for (PythonParser.TestContext temCtx : ctx.test()) {
				temStr = temStr + "," + visit(temCtx);
			}
			return temStr;
		}
		@Override
		public String visitName(PythonParser.NameContext ctx) {
			//  : NAME
			//    | TRUE
			//    | FALSE
			//    ;
			if (ctx.NAME() != null) {
				useList.add(ctx.NAME().getText());
				defList.add(ctx.NAME().getText());
                return ctx.NAME().getText();
            }
            return "";
		}
		@Override
		public String visitNumber(PythonParser.NumberContext ctx) {
			// number
			//    : integer
			//    | IMAG_NUMBER
			//    | FLOAT_NUMBER
			//    ;
			return ctx.getText();
		}
		@Override
		public String visitTest(PythonParser.TestContext ctx) {
			// : logical_test (IF logical_test ELSE test)?
			//    | LAMBDA varargslist? COLON test
			//    ;
			if (ctx.logical_test() != null) {
				// the first situation
				for (PythonParser.Logical_testContext temCtx : ctx.logical_test()) {
					visit(temCtx);
				}
				if (ctx.test() != null) {
					visit(ctx.test());
				}
			} else if (ctx.LAMBDA() != null) {
				// the second situation
				if (ctx.varargslist() != null)
					return  "lambda " + visit(ctx.varargslist()) + " : " + visit(ctx.test());
			} else {
				// null
				return "";
			}
			return "";
		}

		/**
		 * Check to see if the given expression is USABLE.
		 * An expression is usable if we are required to add it to the USE-list.
		 * Any expression who is DEFINABLE should be added to the USE-list.
		 * An expression is definable, if it holds a value which can be modified in the program.
		 * For example, Class names and Class types are not definable.
		 * Method invocations are not definable.
		 * Literals are also not definable.
		 */
		private boolean isUsableExpression(String expr) {
			// must not be a literal or of type 'class'.
			// must not be a method-call or parenthesized expression
			if (expr.endsWith(")"))
				return false;
			// must not be an array-indexing expression
			if (expr.endsWith("]"))
				return false;
			// must not be post unary operation expression
			if (expr.endsWith("++") || expr.endsWith("--"))
				return false;
			// must not be a pre unary operation expression
			if (expr.startsWith("+") || expr.startsWith("-") || expr.startsWith("!") || expr.startsWith("~"))
				return false;
			// must not be an array initialization expression
			if (expr.endsWith("}"))
				return false;
			// must not be an explicit generic invocation expression
			if (expr.startsWith("<"))
				return false;
			//
			if (expr.startsWith(","))
				return false;
			//
			if (expr.endsWith(","))
				return false;
			//
			if(expr.equals(""))
				return false;
			return true;
		}

		/**
		 * Get the original program text for the given parser-rule context.
		 * This is required for preserving whitespaces.
		 */
		private String getOriginalCodeText(ParserRuleContext ctx) {
			int start = ctx.start.getStartIndex();
			int stop = ctx.stop.getStopIndex();
			Interval interval = new Interval(start, stop);
			return ctx.start.getInputStream().getText(interval);
		}
		/**
		 * transfor the with statement to String
		 */
		private String tranWithItem(ParserRuleContext ctx){
			assert ctx instanceof PythonParser.With_itemContext;
			String code = ((PythonParser.With_itemContext) ctx).test().getText();
			if (((PythonParser.With_itemContext) ctx).AS() != null){
				code = code + " as " + ((PythonParser.With_itemContext) ctx).expr().getText();
			}
			return code;
		}
	}
}
/**
 * A simple structure to store DEF information about a Python method.
 *
 * @author WenLiang Peng
 */
class MethodDefInfo {

	// Method ID
	public final String NAME;                   // method name
	public final String RET_TYPE;               // method return type
	public final String CLASS_NAME;             // belong to which class name

	// DEF Info
	private boolean stateDEF;
	private boolean[] argDEFs;
	private List<String> fieldDEFs;         // including the self var

	public MethodDefInfo(String ret, String name, String cls) {
		NAME = name;
		RET_TYPE = ret;
		CLASS_NAME = cls;
		//
		fieldDEFs = new ArrayList<>();
		stateDEF = guessByTypeOrName();
	}

	private boolean guessByTypeOrName() {
		// First check if this method is a constructor ...
		if (RET_TYPE == null)
			return true;
		// If not, then try to guess by method-name ...
		String[] prefixes = { "set", "put", "add", "insert", "push", "append" };
		for (String pre: prefixes)
			if (NAME.toLowerCase().startsWith(pre))
				return true;
		return false;
	}

	public boolean doesStateDEF() {
		return stateDEF;
	}

	public void setStateDEF(boolean stateDef) {
		stateDEF = stateDef;
	}

	public boolean[] argDEFs() {
		return argDEFs;
	}

	public void setArgDEF(int argIndex, boolean def) {
		argDEFs[argIndex] = def;
	}

	public void setAllArgDEFs(boolean[] argDefs) {
		argDEFs = argDefs;
	}

	public String[] fieldDEFs() {
		return fieldDEFs.toArray(new String[fieldDEFs.size()]);
	}

	public void addFieldDEF(String fieldName) {
		if (!fieldDEFs.contains(fieldName)) {
			fieldDEFs.add(fieldName);
			stateDEF = true;
		}
	}

	@Override
	public boolean equals(Object obj) {
		if (!(obj instanceof MethodDefInfo))
			return false;
		MethodDefInfo info = (MethodDefInfo) obj;
		return this.NAME.equals(info.NAME) && this.CLASS_NAME.equals(info.CLASS_NAME)
				&& this.RET_TYPE.equals(info.RET_TYPE);
	}

	@Override
	public String toString() {
		String retType = RET_TYPE == null ? "null" : RET_TYPE;
		StringBuilder str = new StringBuilder();
		str.append("{ TYPE : \"").append(retType).append("\", ");
		str.append("NAME : \"").append(NAME).append("\", ");
		str.append("CLASS : \"").append(CLASS_NAME).append("\", ");
		return str.toString();
	}
}